package com.sy.socket.udp;

import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * UDP搜索者，用于搜索服务支持方
 */
public class UDPSearcher {

    private static final int LISTEN_PORT = 30000;

    public static void main(String[] args) throws IOException, InterruptedException {
        System.out.println("UDPSearcher starts");

        Listener listener = listen();
        sendBroadcast();


        // 读取键盘任意字符就退出
        System.in.read();

        List<Device> devices = listener.getDevicesAndClose();

        for (Device device : devices) {
            System.out.println("Device：" + device.toString());
        }

        // 完成
        System.out.println("UDPSearcher Finished");

    }

    public static Listener listen() throws InterruptedException {

        System.out.println("UDPSearcher start listen");

        CountDownLatch countDownLatch = new CountDownLatch(1);
        Listener listener = new Listener(LISTEN_PORT, countDownLatch);
        listener.start();

        countDownLatch.await();
        return listener;

    }

    private static void sendBroadcast() throws IOException {

        System.out.println("UDPSearcher sendBroadcast started");

        // 搜索方，无需指定端口，系统会直接分配
        DatagramSocket ds = new DatagramSocket();

        // 向接收方发送数据
        // 构建请求数据
        String requestData = MessageCreator.buildWithPort(LISTEN_PORT);
        byte[] requestDataBytes = requestData.getBytes();
        // 构建Packet
        DatagramPacket reqPacket = new DatagramPacket(requestDataBytes,
                requestDataBytes.length);
        // 目标：广播地址 + 20000端口
        reqPacket.setAddress(InetAddress.getByName("255.255.255.255")); // 发送到广播地址
        reqPacket.setPort(20000);
        // 发送
        ds.send(reqPacket);

        ds.close();

        System.out.println("UDPSearcher sendBroadcast finished");


    }

    /**
     * 设备的类
     */
    private static class Device {
        final int port;
        final String ip;
        final String sn;

        public Device(int port, String ip, String sn) {
            this.port = port;
            this.ip = ip;
            this.sn = sn;
        }

        @Override
        public String toString() {
            return "Device{" +
                    "port=" + port +
                    ", ip='" + ip + '\'' +
                    ", sn='" + sn + '\'' +
                    '}';
        }
    }

    /**
     * 构造监听类
     */
    public static class Listener extends Thread {

        private final int listenPort;

        private final CountDownLatch countDownLatch;

        private final List<Device> devices = new ArrayList<>();

        private boolean done = false;

        private DatagramSocket ds = null;




        public Listener(int listenPort, CountDownLatch countDownLatch) {
            super();
            this.listenPort = listenPort;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            super.run();

            // 通知已经启动
            countDownLatch.countDown();

            try {
                ds = new DatagramSocket(listenPort);

                while (!done) {

                    // 接收提供者返回回来的数据
                    final byte[] buf = new byte[512];
                    DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length);

                    // 接收数据
                    ds.receive(datagramPacket);

                    // 打印接收到的信息和提供者的信息
                    // 提供者的ip地址
                    String ip = datagramPacket.getAddress().getHostAddress();
                    // 提供者的端口
                    int port = datagramPacket.getPort();
                    // 提供者的数据长度
                    int dataLen = datagramPacket.getLength();
                    String data = new String(datagramPacket.getData(), 0, dataLen);
                    System.out.println("UDPSearcher receive from ip：" + ip
                            + "\tport：" + port + "\tdata：" + data);

                    // 自定义规则解析sn
                    String sn = MessageCreator.parseSn(data);
                    if (sn != null) {
                        Device device = new Device(port, ip, sn);
                        devices.add(device);
                    }
                }

            } catch (Exception ignored) {
            } finally {
                close();
            }
            System.out.println("UDPSearcher listener finished");
        }

        private void close() {
            if (ds != null) {
                ds.close();
                ds = null;
            }
        }

        List<Device> getDevicesAndClose() {
            done = true;
            close();
            return devices;
        }
    }
}
